%{

using namespace std;

#include <cstring>
#include <cstdio>
#include <cstdlib>

#include <iostream>
#include <vector>
#include <list>
#include <string>

#include "attribute.h"
#include "base_element.h"
#include "element.h"
#include "special_element.h"
#include "base_sub_element.h"
#include "sub_element.h"
#include "node_element.h"
#include "data_element.h"
#include "choice.h"
#include "sequence.h"
#include "multiplicity.h"
#include "doctype_definition.h"
#include "dtd.tab.h"

void dtderror(DoctypeDefinition* doctypeDef, char *msg);
int dtdwrap(void);
int dtdlex(void);

%}

%union {
	char *s;
	DoctypeDefinition* dtd;
	Multiplicity* mult;
	std::list<Attribute*>* attlist;
	Attribute* att;
	BaseSubElement* bse;
	SubElement* se;
}

%token ELEMENT ATTLIST SUP OUVREPAR FERMEPAR VIRGULE BARRE FIXED EMPTY ANY PCDATA AST PTINT PLUS CDATA
%token <s> NOM TOKENTYPE DECLARATION VALEUR
%type <dtd> main dtd_list_opt
%type <attlist> att_definition_opt
%type <mult> card_opt
%type <att> attribut
%type <bse> cp 
%type <se> elem_definition choice sub_choice sequence sub_sequence_opt

%parse-param {DoctypeDefinition* doctypeDef}

%%

main: dtd_list_opt {
	$$ = $1;
}
;

dtd_list_opt
: dtd_list_opt ATTLIST NOM att_definition_opt SUP {
	$$ = $1;
	string name = string($3);
	BaseElement* e = $$->getElementByName(name);
	if (e == NULL) {
		cout << "attribute  declaration not associated with any element...Ignored for now\n";
	} else {
		e->setAttList(*$4);
	}
}
| dtd_list_opt ELEMENT NOM elem_definition SUP {
	$$ = $1;
	string name = string($3);
	Element* e = new Element(name, $4);
	$$->addDeclaration(name, e);
}
| dtd_list_opt ELEMENT NOM ANY SUP {
	$$ = $1;
	string name = string($3);
	SpecialElement* e = new SpecialElement(name, SpecialElement::ANY); 
	$$->addDeclaration(name, e);
}
| dtd_list_opt ELEMENT NOM EMPTY SUP {
	$$ = $1;
	string name = string($3);
	SpecialElement* e = new SpecialElement(name, SpecialElement::EMPTY);
	$$->addDeclaration(name, e);
}
| /* vide */ {
	$$ = doctypeDef;
}
;

att_definition_opt
: att_definition_opt attribut {
	$$ = $1;
	$$->push_back($2);
}
| /* vide */ {
	$$ = new list<Attribute*>();
}
;

attribut
: NOM att_type defaut_declaration {
	string name = string($1);
	$$ = new Attribute(name, Attribute::CDATA, Attribute::IMPLIED); //TODO add other types
}
;

att_type
: CDATA
| TOKENTYPE
| type_enumere
;

type_enumere
: OUVREPAR liste_enum_plus FERMEPAR
;

liste_enum_plus
: liste_enum BARRE NOM
;

liste_enum
: NOM
| liste_enum BARRE NOM
;

defaut_declaration
: DECLARATION
| VALEUR
| FIXED VALEUR
;

elem_definition
: choice card_opt {
	$$ = $1;
	$$->addMultiplicity(*$2);
}
| sequence card_opt {
	$$ = $1;
	$$->addMultiplicity(*$2);
}
;

choice
: OUVREPAR cp sub_choice FERMEPAR {
	$$ = $3;
	$$->addChildFirst(*$2);
}
;

sub_choice
: sub_choice BARRE cp {
	$$ = $1;
	$$->addChild(*$3);
}
| BARRE cp {
	$$ = new Choice();
	$$->addChild(*$2);
}
;

sequence
: OUVREPAR cp sub_sequence_opt FERMEPAR {
	$$ = $3;
	$$->addChildFirst(*$2);
}
;

sub_sequence_opt
: sub_sequence_opt VIRGULE cp {
	$$ = $1;
	$$->addChild(*$3);
}
| /* vide */ {
	$$ = new Sequence();
}
;

cp
: NOM card_opt {
	string name = string($1);
	$$ = new NodeElement(name, *$2);
}
| PCDATA {
	$$ = new DataElement();
}
| elem_definition
;

card_opt
: PTINT {
	$$ = new Multiplicity(0, 1);
}
| AST {
	$$ = new Multiplicity(0, Multiplicity::INF);
}
| PLUS {
	$$ = new Multiplicity(1, Multiplicity::INF);
}
| /* vide */ {
	$$ = new Multiplicity(1, 1);
}
;

%%

#ifdef STANDALONE

int main(int argc, char **argv) {
	int err;

  dtddebug = 1; // pour désactiver l'affichage de l'exécution du parser LALR, commenter cette ligne

	DoctypeDefinition* dd = new DoctypeDefinition();
  err = dtdparse(dd);
  if (err != 0) {
  	printf("Parse ended with %d error(s)\n", err);
  } else {
  	printf("Parse ended with success\n");
  	dd->print(cout);
  }
  delete dd;
  return 0;
}

#endif

int dtdwrap(void) {
  return 1;
}

void dtderror(DoctypeDefinition* doctypeDef, char *msg) {
  fprintf(stderr, "%s\n", msg);
}
